/*  This file is part of CASpp.

    CASpp is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    CASpp is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with CASpp.  If not, see <http://www.gnu.org/licenses/>.

    Copyright 2014 Jérome LAURENS

    See https://code.google.com/p/idalgo/

*/

#if !defined _CASPP_MODELS_TEMPLATES_I_
#define _CASPP_MODELS_TEMPLATES_I_

#include "models.h"
#include <iostream>

CASPP_NS_BEGIN

template <typename T>
const std::string Description<T>::operator() (const Boolean & __x) {
    return __x.description();
}

template <typename T>
const std::string Description<T>::operator()(const String & n) {
    return n;
}

template <typename T>
const std::string Description<T>::operator()(const List & n) {
    return description(n);
}

template <typename T>
const std::string Description<T>::operator()(const Vector & n) {
    return description(n);
}

template <typename T>
const std::string Description<T>::operator()(const Set & n) {
    return description(n);
}

template <typename T>
const std::string Description<T>::operator()(const Map & n) {
    return description(n);
}

template <typename T>
const std::string Description<T>::operator() (const Integer & __x) {
    return description(__x);
}

template <typename T>
const std::string Description<T>::operator() (const Integer & __x, const int base) {
    return description(__x,base);
}

template <typename T>
const std::string Description<T>::operator() (const Rational & __x) {
    return description(__x);
}
template <typename T>
const std::string Description<T>::operator() (const Rational & __x, const int base) {
    return description(__x, base);
}
template <typename T>
const std::string Description<T>::operator()(const Real & __x) {
    return operator()(__x,false,T());
}
template <typename T>
const std::string Description<T>::operator()(const Real & __x
                                    , bool wrap
                                    , const T & getNumberDot
                                    , const size_t n
                                    , const Rounding::Values rnd
                                    , const int base) {
    CASppParameterAssert(base>1 && base <63);
    size_t _n = n>1? n: 0;
    const mpfr_t & op = __x.backend().data();
    mpfr_exp_t expptr = 0;
    char * s = mpfr_get_str(0,&expptr,base,_n,op,mpfr_rnd_t(rnd));
    if (!s) {
        return "ERROR";
    }
    boost::shared_ptr<char> p(s,mpfr_free_str);// free at end of scope
    size_t s_len = strlen(s);
    // intentionally round for too many intermediate 0's or 9
    if (0==_n && rnd==Rounding::Significant) {
        const size_t truncate = log2f(base);
        if (s_len>truncate) {
            char nine = base-1;
            size_t i = truncate;
            // get first '0' char from the end
            while(i++<s_len) {
                if ('0' == s[s_len-i]) {
                    size_t j = i;
                    while(j++<s_len && ('0' == s[s_len-j])) {}
                    if (j>3*i) {
                        s_len -= j;
                        if (s_len == 0) ++s_len;// the 0 case
                        s[++s_len] = 0;
                        break;
                    }
                } else if (nine == s[s_len-i]) {
                    size_t j = i;
                    while (true) {
                        if (j++<s_len) {
                            if (nine != s[s_len-j]) {
                                if (j>3*i) {
                                    _n = s_len-j+1;
                                    if (_n==1) _n = 2;
                                    s = mpfr_get_str(0,&expptr,base,_n,op,mpfr_rnd_t(rnd));
                                    p.reset(s);
                                    break;
                                }
                            }
                        } else {
                            // only 'nines'
                            _n = 2;
                            s = mpfr_get_str(0,&expptr,base,_n,op,mpfr_rnd_t(rnd));
                            p.reset(s);
                            break;
                        }
                    }
                }
            }
        }
    }
    std::ostringstream stream;
    std::string out;
    if (s[0]=='-') {
        stream << "-";
        ++s;
    }
    const char dot = getNumberDot();
    if (expptr < 0) {
        stream << "0" << dot << s;
        stream << "×" << base;// the times separator character implies a wrapper, eventually
        if (base == 10) {
            std::string s;
            mpfr_exp_t e = - expptr;
            do {
                switch(e%10) {
                case 0: s=std::string("⁰")+s;break;
                case 1: s=std::string("¹")+s;break;
                case 2: s=std::string("²")+s;break;
                case 3: s=std::string("³")+s;break;
                case 4: s=std::string("⁴")+s;break;
                case 5: s=std::string("⁵")+s;break;
                case 6: s=std::string("⁶")+s;break;
                case 7: s=std::string("⁷")+s;break;
                case 8: s=std::string("⁸")+s;break;
                case 9: s=std::string("⁹")+s;break;
                }
            } while ((e/=10)>0);
            stream << "⁻" << s;
        } else {
            Integer i(expptr);
            stream << "^" << Description()(i,base);
        }
        out = wrap ?
                    std::string("(")+stream.str()+std::string(")"):
                    stream.str();
    } else if (expptr == 0) {
        stream << "0" << dot << s;
        out = stream.str();
    } else if (expptr <= s_len) {
        stream << std::string(s,expptr) << dot << s+expptr;
        out = stream.str();
    } else {
        stream << "0" << dot << s;
        stream << "×" << base;// the space character needs wrapper, eventually
        if (base == 10) {
            std::string s;
            mpfr_exp_t e = expptr;
            do {
                switch(e%10) {
                case 0: s=std::string("⁰")+s;break;
                case 1: s=std::string("¹")+s;break;
                case 2: s=std::string("²")+s;break;
                case 3: s=std::string("³")+s;break;
                case 4: s=std::string("⁴")+s;break;
                case 5: s=std::string("⁵")+s;break;
                case 6: s=std::string("⁶")+s;break;
                case 7: s=std::string("⁷")+s;break;
                case 8: s=std::string("⁸")+s;break;
                case 9: s=std::string("⁹")+s;break;
                }
            } while ((e/=10)>0);
            stream << s;
        } else {
            Integer i(expptr);
            stream << "^" << Description()(i,base);
        }
        out = wrap ?
                    std::string("(")+stream.str()+std::string(")"):
                    stream.str();
    }
    return out;
}
template <typename T>
const std::string Description<T>::operator()(const Symbol & n) {
    return n.description();
}
template <typename T>
const std::string Description<T>::operator()(const Variable & n) {
    return n.description();
}
template <typename T>
const std::string Description<T>::operator()(const Function & n) {
    return n.description();
}

CASPP_NS_END

#endif // _CASPP_MODELS_TEMPLATES_I_
